package zhoug.zui.map;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;

import zhoug.logging.Logger;
import zhoug.zui.R;

import java.text.MessageFormat;
import java.util.List;

import androidx.annotation.Nullable;

/**
 * 中国地图
 *
 * @Author 35574
 * @Date 2020/9/24
 * @Description
 */
public class ZUIChinaMapView extends View {
    private static final String TAG = ">>>ChinaMapView";
    private static final boolean DEBUG = false;
    private Paint mPaint;
    private int borderWidth;//边界宽度
    private int borderColor;//地图省市边界色
    private boolean drawFill;//是否填充区域，默认true
    private int fillColor;//默认填充色,可以被区域自己的填充色贴换
    private int highlightColor;//高亮色
    private int areaTextColor ;//区域文本颜色
    private int areaTextSize = 10;//区域文本大小

    private boolean drawAreaText = true;//是否绘制区域文本
    private boolean scaleEnable = false;//是否可以缩放
    private boolean scrollEnable = false;//是否可以滚动
    private boolean highlightEnable = false;//是否启用高亮,启用后点击区域切换高亮状态

    private List<ChinaPath> mChinaPaths;//区域路径
    private GestureDetector mGestureDetector;
    private ScaleGestureDetector mScaleGestureDetector;

    private List<Marker> markers;


    public ZUIChinaMapView(Context context) {
        this(context, null);
    }

    public ZUIChinaMapView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ZUIChinaMapView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    }

    private void init(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ZUIChinaMapView, defStyleAttr, R.style.ZUIChinaMapView_def);
        borderColor = a.getColor(R.styleable.ZUIChinaMapView_zui_borderColor, Color.WHITE);
        borderWidth = a.getDimensionPixelOffset(R.styleable.ZUIChinaMapView_zui_borderWidth, 2);
        drawFill = a.getBoolean(R.styleable.ZUIChinaMapView_zui_drawFill, false);
        fillColor = a.getColor(R.styleable.ZUIChinaMapView_zui_fillColor, Color.BLUE);
        highlightColor = a.getColor(R.styleable.ZUIChinaMapView_zui_highlightColor, Color.RED);
        drawAreaText = a.getBoolean(R.styleable.ZUIChinaMapView_zui_drawAreaText, false);
        areaTextColor = a.getColor(R.styleable.ZUIChinaMapView_zui_areaTextColor, Color.BLACK);

        scaleEnable = a.getBoolean(R.styleable.ZUIChinaMapView_zui_scaleEnable, false);
        scrollEnable = a.getBoolean(R.styleable.ZUIChinaMapView_zui_scrollEnable, false);
        highlightEnable = a.getBoolean(R.styleable.ZUIChinaMapView_zui_highlightEnable, false);

        a.recycle();

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setAntiAlias(true);
        mChinaPaths = ChinaPath.Factory.createAllPath();
        initMapBounds();
        //单击,移动事件
        mGestureDetector = new GestureDetector(context, onGestureListener);
        //缩放事件
        mScaleGestureDetector = new ScaleGestureDetector(context, onScaleGestureListener);

    }

    private Matrix mMatrix = new Matrix();
    //    private float startX;//开始画地图的起点x
//    private float startY;//开始画地图的起点y
    private float initScale;// 初始时的缩放级别
    private float minScale = 0.2f;//最小缩放 在inScale的基础上
    private float maxScale = 6;//最大缩放  在inScale的基础上
    private float scale = 1;//当前缩放 在inScale的基础上
    private float realScale;//真实的缩放倍数:当前地图和原初地图的比
    private int mapDefWidth;//地图默认宽,
    private int mapDefHeight;//地图默认高

    private RectF areaBounds = new RectF();//每个区域的边界矩形
    private RectF mapBounds;//地图区域边界矩形


    @Override
    protected void onDraw(Canvas canvas) {
        if (DEBUG) {
            //画地图边界
//            mPaint.setColor(Color.YELLOW);
//            mPaint.setStyle(Paint.Style.STROKE);
//            canvas.drawRect(mapBounds, mPaint);
        }
        mMatrix.setScale(realScale, realScale);
        canvas.translate(mapBounds.left, mapBounds.top);
        canvas.concat(mMatrix);
        //绘制地图底图
        if (mChinaPaths != null && mChinaPaths.size() > 0) {
            //绘制省市
            ChinaPath chinaPath;
            Path path;
            for (int i = 0; i < mChinaPaths.size(); i++) {
                chinaPath = mChinaPaths.get(i);
                path = chinaPath.getPath();
                //绘制边界
                mPaint.setStyle(Paint.Style.STROKE);
                mPaint.setStrokeWidth(borderWidth);
                mPaint.setColor(borderColor);
                canvas.drawPath(path, mPaint);
                //绘制高亮色
                if (chinaPath.isHighlight()) {
                    mPaint.setStyle(Paint.Style.FILL);
                    if (chinaPath.getHighlightColor() != 0) {
                        mPaint.setColor(chinaPath.getHighlightColor());
                    } else {
                        mPaint.setColor(highlightColor);
                    }
                    canvas.drawPath(path, mPaint);
                } else if (drawFill) {
                    //绘制填充色
                    mPaint.setStyle(Paint.Style.FILL);
                    if (chinaPath.getFillColor() != 0) {
                        mPaint.setColor(chinaPath.getFillColor());
                    } else {
                        mPaint.setColor(fillColor);
                    }
                    canvas.drawPath(path, mPaint);
                }

                //绘制文本
                if (drawAreaText) {
                    mPaint.setStyle(Paint.Style.FILL);
                    mPaint.setColor(areaTextColor);
                    mPaint.setTextSize(areaTextSize);
                    mPaint.setTextAlign(Paint.Align.CENTER);
                    Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
                    path.computeBounds(areaBounds, true);
                    float x = chinaPath.getCenterX() != 0 ? chinaPath.getCenterX() : (areaBounds.left + areaBounds.right) / 2;
                    float y = (chinaPath.getCenterY() != 0 ? chinaPath.getCenterY() : (areaBounds.bottom + areaBounds.top) / 2) - (fontMetrics.descent + fontMetrics.ascent) / 2;
                    canvas.drawText(chinaPath.getArea().name, x, y, mPaint);
                }


            }
        }
        drawMarker(canvas);
    }

    /**
     * 绘制maker
     *
     * @param canvas
     */
    private void drawMarker(Canvas canvas) {
        if (markers != null && markers.size() > 0) {
            Marker marker;
            ChinaPath chinaPath;
            Path path;
            for (int i = 0; i < markers.size(); i++) {
                marker = markers.get(i);
                chinaPath = mChinaPaths.get(marker.getChinaArea().value);
                path = chinaPath.getPath();
                //绘制maker
                if (DEBUG) {
                    Logger.d(TAG, "onDraw:绘制maker");
                }
                path.computeBounds(areaBounds, true);
                int centerX = (int) (chinaPath.getCenterX() != 0 ? chinaPath.getCenterX() : (areaBounds.left + areaBounds.right) / 2);
                int centerY = (int) ((chinaPath.getCenterY() != 0 ? chinaPath.getCenterY() : (areaBounds.bottom + areaBounds.top) / 2));
                Drawable icon = marker.getIcon();
                if (icon != null) {
                    int xOffset = icon.getIntrinsicWidth() >> 1;
                    int yOffset = icon.getIntrinsicHeight() >> 1;
                    icon.setBounds(centerX - xOffset, centerY - yOffset, centerX + xOffset, centerY + yOffset);
                    icon.draw(canvas);
                    centerY += yOffset;
                }
                if (marker.getText() != null) {
                    mPaint.setStyle(Paint.Style.FILL);
                    mPaint.setColor(marker.getTextColor() != 0 ? marker.getTextColor() : areaTextColor);
                    float testSize = marker.getTextSize() != 0 ? marker.getTextSize() / initScale : areaTextSize;
                    mPaint.setTextSize(testSize);
                    if (DEBUG) {
                        Logger.d(TAG, "drawMarker:testSize=" + testSize);
                    }
                    Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
                    float h = fontMetrics.descent - fontMetrics.ascent;
                    mPaint.setTextAlign(Paint.Align.CENTER);
                    if (DEBUG) {
                        Logger.d(TAG, "drawMarker:h=" + h);
                    }
                    canvas.drawText(marker.getText(), centerX, centerY + h, mPaint);
                }
            }

        }


    }


    /**
     * 初始化 计算地图边界
     * 1.黑龙江是中国最东，最北的省份
     * 2.新疆是中国最西的省份
     * 3.海南是中国最南的省份
     * <p/>
     * 地图边界为
     * 0点                  1点
     * 0,0------------------heilongjiang.right,0
     * |                      |
     * |                      |
     * 0,hainan.bottom------heilongjiang.right,hainan.bottom
     * 3点                   2点
     * 地图宽度--->heilongjiang.right
     * 地图高度--->hainan.bottom
     */
    private void initMapBounds() {
        //r
        RectF hljRF = new RectF();
        mChinaPaths.get(ChinaArea.HeiLongJiang.value).getPath().computeBounds(hljRF, true);
        if (DEBUG) {
            Logger.d(TAG, "computeBounds:" + hljRF);
        }
        //b
        RectF hnRF = new RectF();
        mChinaPaths.get(ChinaArea.HaiNan.value).getPath().computeBounds(hnRF, true);
        if (DEBUG) {
            Logger.d(TAG, "computeBounds:" + hnRF);
        }
        mapBounds = new RectF();
        mapBounds.left = 0;
        mapBounds.top = 0;
        mapBounds.right = hljRF.right;
        mapBounds.bottom = hnRF.bottom;

        //地图宽高
        mapDefWidth = (int) (hljRF.right + 0.5f);
        mapDefHeight = (int) (hnRF.bottom + 0.5f);
        if (DEBUG) {
            Logger.d(TAG, "initMapBounds:" + MessageFormat.format("mapDefWidth={0},mapDefHeight={1}", mapDefWidth, mapDefHeight));
        }
    }

    private String getModeString(int measureSpec){
        switch (MeasureSpec.getMode(measureSpec)){
            case MeasureSpec.AT_MOST:
                return "AT_MOST";
            case MeasureSpec.EXACTLY:
                return "EXACTLY";
            case MeasureSpec.UNSPECIFIED:
                return "UNSPECIFIED";
        }
        return null;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int paddingH = getPaddingStart() + getPaddingEnd();
        int paddingV = getPaddingTop() + getPaddingBottom();
//        int width = measureSize(widthMeasureSpec, getSuggestedMinimumWidth(), mapDefWidth + paddingH);
//        int height = measureSize(heightMeasureSpec, getSuggestedMinimumHeight(), mapDefHeight + paddingV);
        int width = 0;
        int height = 0;
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        if (DEBUG) {
            Logger.d(TAG, "onMeasure:widthSize="+widthSize+",heightSize="+heightSize);
            Logger.d(TAG, "onMeasure:widthMode="+getModeString(widthMeasureSpec)+",heightMode="+getModeString(heightMeasureSpec));
        }

        if (widthMode != MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) {
            //宽度匹配内容，高度已知
            height = measureSize(heightMeasureSpec, getSuggestedMinimumHeight(), mapDefHeight + paddingV);
            float scaleY=(float) (height - paddingV) / mapDefHeight;
            if(widthSize>0){
                float scaleX = (float) (widthSize - paddingH) / mapDefWidth;
                float  scale=Math.min(scaleX,scaleY);
                width= (int) (scale*mapDefWidth+paddingH);
            }else{
                width= (int) (scaleY*mapDefWidth+paddingH);
            }

        } else if (heightMode != MeasureSpec.EXACTLY && widthMode == MeasureSpec.EXACTLY) {
            //高度匹配内容，宽度已知
            width = measureSize(widthMeasureSpec, getSuggestedMinimumWidth(), mapDefWidth + paddingH);
            if (DEBUG) {
                Logger.d(TAG, "onMeasure:width="+width);
            }
            float scaleX=(float) (width - paddingH) / mapDefWidth;
            if(heightSize>0){
                float scaleY= (float) (heightSize - paddingV) / mapDefHeight;
                float scale=Math.min(scaleX,scaleY);
                height= (int) (scale*mapDefHeight+paddingV);
            }else{
                height= (int) (scaleX*mapDefHeight+paddingV);
            }
        } else {
            width = measureSize(widthMeasureSpec, getSuggestedMinimumWidth(), mapDefWidth + paddingH);
            height = measureSize(heightMeasureSpec, getSuggestedMinimumHeight(), mapDefHeight + paddingV);
        }


        if (DEBUG) {
            Logger.d(TAG, "onMeasure:width=" + width + ",height=" + height);
        }
        float scaleX = (float) (width - paddingH) / mapDefWidth;
        float scaleY = (float) (height - paddingV) / mapDefHeight;
        initScale = Math.min(scaleX, scaleY);
        realScale = scale * initScale;
        if (DEBUG) {
            Logger.d(TAG, "onMeasure:initScale=" + initScale + ",scale=" + scale + ",realScale=" + realScale);
        }
        areaTextSize = width / 80;
        setMeasuredDimension(width, height);
        computeMapBounds();

    }

    /**
     * @param measureSpec
     * @param minSize     最小值
     * @param defSize     默认值
     */
    private int measureSize(int measureSpec, int minSize, int defSize) {
        int result = defSize;
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        switch (mode) {
            case MeasureSpec.UNSPECIFIED:
                result = Math.max(minSize, defSize);
                break;
            case MeasureSpec.EXACTLY:
                result = size;
                break;
            case MeasureSpec.AT_MOST:
                result = Math.max(minSize, defSize);
                if (size > 0) {
                    result = Math.min(result, size);
                }
                break;
        }
        return result;
    }

    /**
     * 计算地图边界,平移和缩放后在Canvas上的区域
     */
    private void computeMapBounds() {
        int mapWidth = (int) (realScale * mapDefWidth + 0.5);
        int mapHeight = (int) (realScale * mapDefHeight + 0.5);
        if (DEBUG) {
            Logger.d(TAG, "onMeasure:mapWidth=" + mapWidth + ",mapHeight=" + mapHeight);
        }
        int startX = getPaddingStart() + ((getMeasuredWidth() - mapWidth - getPaddingStart() - getPaddingEnd()) >> 1);
        int startY = getPaddingTop() + ((getMeasuredHeight() - mapHeight - getPaddingTop() - getPaddingBottom()) >> 1);
        if (DEBUG) {
            Logger.d(TAG, "onMeasure:startX=" + startX + ",startY=" + startY);
        }
        mapBounds.left = startX;
        mapBounds.top = startY;
        mapBounds.right = mapBounds.left + realScale * mapDefWidth;
        mapBounds.bottom = mapBounds.top + realScale * mapDefHeight;
    }

    /**
     * 限制缩放倍数
     *
     * @param newScale
     * @return
     */
    private float limitScale(float newScale) {
        if (newScale != scale) {
            if (newScale > maxScale) {
                return maxScale;
            } else if (newScale < minScale) {
                return minScale;
            } else {
                return newScale;
            }
        }
        return scale;
    }


    /**
     * 放大
     */
    public void scaleBigger() {
        if (scale < maxScale) {
            scale(scale + 0.5f);
        }
    }

    /**
     * 缩小
     */
    public void scaleSmall() {
        if (scale > minScale) {
            scale(scale - 0.5f);
        }
    }

    /**
     * 缩放
     *
     * @param newScale
     */
    private void scale(float newScale) {
        float s = limitScale(newScale);
        if (s != scale) {
            scale = s;
            realScale = scale * initScale;
            if (DEBUG) {
                Logger.d(TAG, "scale:缩放:" + scale + ",realScale=" + realScale);
            }
            computeMapBounds();
            postInvalidate();
            if (scale <= 1) {
                scrollTo(0, 0);
            }
        }
    }

    ////////////触摸事件//////////
    private GestureDetector.SimpleOnGestureListener onGestureListener = new GestureDetector.SimpleOnGestureListener() {

        public boolean onDown(MotionEvent e) {
            return true;
        }

        public boolean onSingleTapUp(MotionEvent event) {
            return onAreaClick(event);
        }

        public void onLongPress(MotionEvent e) {

        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            if (scrollEnable || mapBounds.left < 0 || mapBounds.top < 0) {
                if (DEBUG) {
                    Logger.d(TAG, "onScroll:mapBounds=" + mapBounds);
                    Logger.d(TAG, "onScroll:distanceX=" + distanceX + ",distanceY=" + distanceY);
                }
                scrollTo(getScrollXDistance(distanceX), getScrollYDistance(distanceY));
                return true;
            }
            return false;
        }

        /**
         * 获取需要在x轴移动的距离
         * @param distanceX
         * @return
         */
        private int getScrollXDistance(float distanceX) {
            float x = distanceX + getScrollX();
            if (mapBounds.left <= 0) {
                if (x < mapBounds.left) {
                    x = mapBounds.left;
                } else if (x > mapBounds.right - getWidth()) {
                    x = mapBounds.right - getWidth();
                }

            } else {
                if (x < -mapBounds.left) {
                    x = -mapBounds.left;
                } else if (x > getWidth() - mapBounds.right) {
                    x = getWidth() - mapBounds.right;
                }
            }
            if (DEBUG) {
                Logger.d(TAG, "getScrollXDistance:x=" + x);
            }
            return (int) x;
        }

        /**
         * 获取需要在Y轴移动的距离
         * @param distanceY
         * @return
         */
        private int getScrollYDistance(float distanceY) {
            float y = distanceY + getScrollY();
            if (mapBounds.top <= 0) {
                if (y < mapBounds.top) {
                    y = mapBounds.top;
                } else if (y > mapBounds.bottom - getHeight()) {
                    y = mapBounds.bottom - getHeight();
                }
            } else {
                if (y < -mapBounds.top) {
                    y = -mapBounds.top;
                } else if (y > getHeight() - mapBounds.bottom) {
                    y = getHeight() - mapBounds.bottom;
                }
            }

            if (DEBUG) {
                Logger.d(TAG, "getScrollXDistance:y=" + y);
            }
            return (int) y;
        }


    };

    /**
     * 区域单击事件
     *
     * @param event
     * @return
     */
    private boolean onAreaClick(MotionEvent event) {
        if (highlightEnable) {
            float x = event.getX();
            float y = event.getY();
            if (DEBUG) {
                Logger.d(TAG, "onAreaClick:in " + x + "," + y);
            }
            //根据缩放和偏移量计算出对应的在区域中的坐标
            int x1 = (int) ((x - mapBounds.left + getScrollX()) / realScale);
            int y1 = (int) ((y - mapBounds.top + getScrollY()) / realScale);
            if (DEBUG) {
                Logger.d(TAG, "onAreaClick:x1=" + x1 + ",y1=" + y1);
            }
            if (x < 0 || y1 < 0) {
                return false;
            }
            RectF r = new RectF();
            Region rg = new Region();
            for (ChinaPath chinaPath : mChinaPaths) {
                Path path = chinaPath.getPath();
                path.computeBounds(r, true);
                rg.setPath(path, new Region((int) r.left, (int) r.top, (int) r.right, (int) r.bottom));
                if (rg.contains(x1, y1)) {
                    chinaPath.setHighlight(!chinaPath.isHighlight());
                    invalidate();
                    return true;
                }
            }
        }

        return false;
    }

    private ScaleGestureDetector.SimpleOnScaleGestureListener onScaleGestureListener = new ScaleGestureDetector.SimpleOnScaleGestureListener() {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            if (scaleEnable) {
                float scaleFactor = detector.getScaleFactor();
                float newScale = realScale * scaleFactor / initScale;
                if (DEBUG) {
                    Logger.d(TAG, "scale:scaleFactor=" + scaleFactor + ",newScale=" + newScale + ",realScale=" + realScale);
                }
                scale(newScale);
                return true;
            }

            return super.onScale(detector);
        }

        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            return super.onScaleBegin(detector);
        }

        @Override
        public void onScaleEnd(ScaleGestureDetector detector) {
            super.onScaleEnd(detector);
        }
    };

    private MotionEvent firstDown;
    private int pointCount = 0;//屏幕上的点的个数

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);

        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN://在第一个点被按下时触发
                mGestureDetector.onTouchEvent(event);
                mScaleGestureDetector.onTouchEvent(event);
                firstDown = event;
                pointCount = 1;
                break;
            case MotionEvent.ACTION_POINTER_DOWN://当屏幕上已经有一个点被按住，此时再按下其他点时触发
                pointCount += 1;
                mScaleGestureDetector.onTouchEvent(event);
                break;
            case MotionEvent.ACTION_MOVE://当有点在屏幕上移动时触发。值得注意的是，由于它的灵敏度很高，而我们的手指又不可能完全静止（即使我们感觉不到移动，但其实我们的手指也在不停地抖动），所以实际的情况是，基本上只要有点在屏幕上，此事件就会一直不停地被触发
                if (DEBUG) {
                    Logger.d(TAG, "onTouchEvent:pointCount=" + pointCount);
                }
                if (pointCount == 1) {
                    mGestureDetector.onTouchEvent(event);
                } else {
                    mScaleGestureDetector.onTouchEvent(event);
                }
                break;
            case MotionEvent.ACTION_POINTER_UP://当屏幕上有多个点被按住，松开其中一个点时触发（即非最后一个点被放开时）。
                pointCount -= 1;
                mScaleGestureDetector.onTouchEvent(event);
                break;
            case MotionEvent.ACTION_UP://当屏幕上唯一的点被放开时触发
                mGestureDetector.onTouchEvent(event);
                mScaleGestureDetector.onTouchEvent(event);
                pointCount = 0;
                break;

        }

        return true;
    }

    public void setMarkers(List<Marker> markers) {
        this.markers = markers;
        postInvalidate();
    }

    public List<Marker> getMarkers() {
        return markers;
    }
}
